// 包含交易类相关的头文件，用于创建交易对象和执行交易操作
#include <Trade/Trade.mqh>
// 包含获取交易品种信息的头文件，用于获取交易品种的各种数据
#include <Trade/SymbolInfo.mqh>


#define STOPLOSS 3//止损
#define DIFFER 0.7//差值
#define DIFFER1 1//差值
#define BIGDIFFER 0.5//开价差

#define PERIOD PERIOD_M15

double TOP_price;	  //最高价
double MINI_price;	  //最低价
int order_flage;  //订单标志位0空1多

int next = 0;


//--- 设置交易品种
// string 是 MQL5 中的字符串类型，symbol 变量用于存储要交易的品种名称
// "XAUUSD" 表示黄金兑美元的交易品种
string symbol = "GOLD";

//--- 设置手数
// double 是 MQL5 中的双精度浮点数类型，lot 变量用于存储交易的手数
// 0.05 表示本次交易的手数为 0.05 手
double lot = 0.01;

//--- 创建交易类对象
// CTrade 是 MQL5 中用于执行交易操作的类，创建一个名为 trade 的 CTrade 对象
CTrade trade;



// 将 UTC 时间转换为中国时间
datetime ConvertToChinaTime(datetime utcTime) 
{
    return utcTime + 8 * 60 * 60; // 8 小时的偏移量（秒）
}
void PriceTimeInspection1(void) 
{
    int positionCount = PositionsTotal();

    if (positionCount > 0) 
	{
        //Print("有单");
//
        // 获取最早订单开仓时间对应的 15 分钟 K 线开盘时间
        datetime earliestOpenTime = TimeCurrent();
        for (int pos = 0; pos < positionCount; pos++) 
		{
            ulong positionTicket = PositionGetTicket(pos);
            datetime openTime = PositionGetInteger(POSITION_TIME);
            if (openTime < earliestOpenTime) 
			{
                earliestOpenTime = openTime;
            }
        }
        datetime kLineOpenTime = iTime(symbol, PERIOD, iBarShift(symbol, PERIOD, earliestOpenTime, false));

        // 转换为中国时间
        datetime chinaKLineOpenTime = ConvertToChinaTime(kLineOpenTime);
        Print("最早订单对应 15 分钟 K 线开盘时间（中国时间）: " + TimeToString(chinaKLineOpenTime));

        // 从该 K 线开始遍历到当前时间，统计最高价和最低价
        int startBar = iBarShift(symbol, PERIOD, kLineOpenTime, false);
        int currentBar = 0;
        TOP_price = iHigh(symbol, PERIOD, startBar);
        MINI_price = iLow(symbol, PERIOD, startBar);
        for (int bar = startBar; bar >= currentBar; bar--) 
		{
            double high = iHigh(symbol, PERIOD, bar);
            double low = iLow(symbol, PERIOD, bar);
        }
        Print("这段历史最高价: " + DoubleToString(TOP_price));
        Print("这段历史最低价: " + DoubleToString(MINI_price));
    }
} 

// 新增函数：订单判断及统计高低价
void PriceTimeInspection(void) 
{
    int positionCount = PositionsTotal();

    if (positionCount > 0) 
	{
        //Print("有单");

        // 获取最早订单开仓时间对应的 15 分钟 K 线开盘时间
        datetime earliestOpenTime = TimeCurrent();
        for (int pos = 0; pos < positionCount; pos++) 
		{
            ulong positionTicket = PositionGetTicket(pos);
            datetime openTime = PositionGetInteger(POSITION_TIME);
            if (openTime < earliestOpenTime) 
			{
                earliestOpenTime = openTime;
            }
        }
        datetime kLineOpenTime = iTime(symbol, PERIOD, iBarShift(symbol, PERIOD, earliestOpenTime, false));

        // 转换为中国时间
        datetime chinaKLineOpenTime = ConvertToChinaTime(kLineOpenTime);
        //Print("最早订单对应 15 分钟 K 线开盘时间（中国时间）: " + TimeToString(chinaKLineOpenTime));

        // 从该 K 线开始遍历到当前时间，统计最高价和最低价
        int startBar = iBarShift(symbol, PERIOD, kLineOpenTime, false);
        int currentBar = 0;
        TOP_price = iHigh(symbol, PERIOD, startBar);
        MINI_price = iLow(symbol, PERIOD, startBar);
        for (int bar = startBar; bar >= currentBar; bar--) 
		{
            double high = iHigh(symbol, PERIOD, bar);
            double low = iLow(symbol, PERIOD, bar);
            if (high > TOP_price) 
			{
                TOP_price = high;
            }
            if (low < MINI_price) 
			{
                MINI_price = low;
            }
        }
        //Print("这段历史最高价: " + DoubleToString(TOP_price));
        //Print("这段历史最低价: " + DoubleToString(MINI_price));
    	}
} 

// 新增函数：平掉所有指定品种的订单
void CloseAllPositions(void) {
    int positionCount = PositionsTotal();
    for (int pos = positionCount - 1; pos >= 0; pos--) {
        ulong positionTicket = PositionGetTicket(pos);
        if (PositionGetString(POSITION_SYMBOL) == symbol) {
            double price;
            if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
                price = SymbolInfoDouble(symbol, SYMBOL_BID);
            } else {
                price = SymbolInfoDouble(symbol, SYMBOL_ASK);
            }
            if (trade.PositionClose(positionTicket, price)) {
                Print("订单 " + IntegerToString(positionTicket) + " 平仓成功");
            } else {
                Print("订单 " + IntegerToString(positionTicket) + " 平仓失败，错误代码: " + IntegerToString(trade.ResultRetcode()));
                Print("错误描述: " + trade.ResultComment());
            }
        }
    }
}

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
// OnInit 函数是专家顾问（EA）初始化时调用的函数，返回一个整数表示初始化结果
int OnInit()
{
/*
    //--- 获取当前市场价格
    // SymbolInfoDouble 是一个函数，用于获取指定交易品种的双精度浮点型信息
    // 第一个参数 symbol 是要查询的交易品种名称，这里是 "XAUUSD"
    // 第二个参数 SYMBOL_ASK 是一个常量，表示要获取的是该品种的卖价（即当前市场上可以买入该品种的价格）
    // 返回值存储在 price 变量中，为双精度浮点数类型
    double price = SymbolInfoDouble(symbol, SYMBOL_ASK);
	TOP_price = price;
	MINI_price = price;

    //--- 执行买入操作
    // trade.Buy 是 CTrade 类的成员函数，用于执行买入交易
    // 第一个参数 lot 是交易的手数，这里是 0.05 手
    // 第二个参数 symbol 是要交易的品种名称，这里是 "XAUUSD"
    // 第三个参数 price 是交易的价格，这里是之前获取的当前市场卖价
    trade.Buy(lot, symbol, price);

    //--- 检查交易结果
    // trade.ResultRetcode 是 CTrade 类的成员函数，用于获取上次交易操作的返回码
    // TRADE_RETCODE_DONE 是一个常量，表示交易操作成功完成
    if (trade.ResultRetcode() != TRADE_RETCODE_DONE)
    {
        // 如果交易失败，使用 Print 函数输出错误信息
        // trade.ResultRetcode() 返回的是交易操作的错误代码
        Print("交易失败，错误代码: ", trade.ResultRetcode());
        // trade.ResultComment 是 CTrade 类的成员函数，用于获取上次交易操作的错误描述信息
        Print("错误描述: ", trade.ResultComment());
    }
    else
    {
        // 如果交易成功，使用 Print 函数输出成功信息和订单号
        // trade.ResultDeal 是 CTrade 类的成员函数，用于获取上次成功交易的订单号
        Print("交易成功，订单号: ", trade.ResultDeal());
    }

    // 判断是否有未平仓订单
    int positionCount = PositionsTotal();
    if (positionCount > 0)
    {
        Print("此时有单");
    }
    else
    {
        Print("现在没有单");
    }
	
	double open1[3];
	double close1[3];
	 ENUM_TIMEFRAMES timeframe = PERIOD;
	for (int i = 0; i < 3; i++)
	{
		open1[i] = iOpen(symbol, timeframe, i + 1);
		close1[i] = iClose(symbol, timeframe, i + 1);
	}
	for (int i = 0; i < 3; i++)
	{
		Print("第 " + IntegerToString(i + 1) + " 根 K 线的开盘价: " + DoubleToString(open1[i]));
		Print("第 " + IntegerToString(i + 1) + " 根 K 线的收盘价: " + DoubleToString(close1[i]));
	}
	Print("===========================\r\n");

	Print("open1[0] == ",open1[0]);//倒数第一根
	Print("open1[1] == ",open1[1]);
	Print("open1[2] == ",open1[2]);

	Print("close1[0] == ",close1[0]);
	Print("close1[1] == ",close1[1]);
	Print("close1[2] == ",close1[2]);
	Print("===========================\r\n");

	// 计算目前持有的黄金手数
    double totalLots = 0;
    for (int pos = 0; pos < positionCount; pos++) 
	{
        ulong positionTicket = PositionGetTicket(pos);
        if (PositionGetString(POSITION_SYMBOL) == symbol) 
		{
            totalLots += PositionGetDouble(POSITION_VOLUME);
        }
    }
    Print("目前持有的黄金手数: ", totalLots);
	
	// 平仓逻辑
	//CloseAllPositions();
*/
	//PriceTimeInspection();

    //---
    // 返回 INIT_SUCCEEDED 表示专家顾问初始化成功
    return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
// OnDeinit 函数是专家顾问（EA）终止时调用的函数
// 参数 reason 是一个整数，表示专家顾问终止的原因
void OnDeinit(const int reason)
{
    //---
    // 这里可以添加一些清理操作，目前为空
}
// 新增函数：判断订单是做空还是做多
int DetermineOrderDirection() {
    int positionCount = PositionsTotal();
    for (int pos = 0; pos < positionCount; pos++) {
        ulong positionTicket = PositionGetTicket(pos);
        if (PositionGetString(POSITION_SYMBOL) == symbol) {
            int positionType = PositionGetInteger(POSITION_TYPE);
            if (positionType == POSITION_TYPE_BUY) {
                return 1; // 做多
            } else if (positionType == POSITION_TYPE_SELL) {
                return 0; // 做空
            }
        }
    }
    return -1; // 没有符合条件的订单
}

int OrderDetermination(void)
{
    int positionCount = PositionsTotal();
	
    if (positionCount > 0)
    {
    	PriceTimeInspection();
		order_flage = DetermineOrderDirection();
		Print("order_flage == ",order_flage);
        Print("111111此时有单");
		return 2;
    }
    else
    {
        Print("222222现在没有单");//就要等这根K线结束
		return 3;
    }
}
//订单判断
int OrderJudgment(void)
{
    int positionCount = PositionsTotal();
	
    if (positionCount > 0)
    {
        //Print("111111此时有单");
		return 1;
    }
    else
    {
        //Print("222222现在没有单");//就要等这根K线结束
		return 0;
    }
}


//判断是否卖出
int PriceJudgment(void)
{

	double price = SymbolInfoDouble(symbol, SYMBOL_ASK);
	
	double value=0;

	/*
	if( price > TOP_price )
	{
		TOP_price = price;
	}
	
	if( price < MINI_price )
	{
		MINI_price = price;
	}
	*/
	if( OrderJudgment() == 0 )//无单就结束这根K线再下订单
	{
		return 3;
	}
	
	PriceTimeInspection(); //遍历从这根K线开始的最低最高价

	if( order_flage ==0)
	{

		value = price - MINI_price;
		if(value>=STOPLOSS)
		{
			PriceTimeInspection1();
			Print("=======================");
			Print("做空");
			Print("value == ",value);
			Print("MINI_price == ",MINI_price);
			Print("price == ",price);
	
			CloseAllPositions();
			Print("777777卖出\r\n");
			return 3;//卖出next
		}
	
	}
	else if( order_flage == 1)
	{
		value = TOP_price - price;
		if(value>=STOPLOSS)
		{
			PriceTimeInspection1();
			Print("=======================");
			Print("做多");
			Print("value == ",value);
			Print("TOP_price == ",TOP_price);
			Print("price == ",price);
			CloseAllPositions();
			Print("8888888卖出\r\n");
			return 3;//卖出next
		}
	}

	return 2;//继续这个循环

}
//+------------------------------------------------------------------+
//| 获取当前15分钟K线的开盘价                                      |
//+------------------------------------------------------------------+
double GetCurrent15MinuteOpenPrice()
{
    // 获取当前 15 分钟 K 线的开盘价，0 表示当前 K 线
    return iOpen(symbol, PERIOD, 0);
}


//是否下单

int PlaceOrder(void)
{
    double open1[3];
	double openPrice=0;
    double close1[3];
	double price=0;
    ENUM_TIMEFRAMES timeframe = PERIOD;
	
    for (int i = 0; i < 3; i++) 
	{
        open1[i] = iOpen(symbol, timeframe, i + 1);
        close1[i] = iClose(symbol, timeframe, i + 1);
    }
	
    for (int i = 0; i < 3; i++) 
	{
        //Print("第 " + IntegerToString(i + 1) + " 根 K 线的开盘价: " + DoubleToString(open1[i]));
        //Print("第 " + IntegerToString(i + 1) + " 根 K 线的收盘价: " + DoubleToString(close1[i]));
    }
	
	if( OrderJudgment() == 1 )//有单就考虑是否卖出
	{
		return 2;
	}
	
	// 调用函数获取当前 15 分钟 K 线的开盘价
	openPrice = GetCurrent15MinuteOpenPrice();

	if( close1[0] > open1[0] && close1[1] > open1[1] && close1[2] > open1[2] )//三阳上升
	{
		price = SymbolInfoDouble(symbol, SYMBOL_ASK);
		// 计算开盘价与 price 的差值的绝对值
		double difference = MathAbs(openPrice - price);
		// 输出开盘价到日志
		Print("当前15分钟K线的开盘价是: ", openPrice);
		// 输出差值的绝对值到日志
		Print("开盘价与 ", price, " 的差值的绝对值是: ", difference);
		
		if( difference >= BIGDIFFER)
		{
			Print("超出了");
			return 3;
		}
		
		trade.Buy(lot, symbol, price);//做多
		TOP_price = price;
		MINI_price = price;
		order_flage = 1;
		Print("333333多\r\n");
		return 2;
	}
	else if( close1[0] < open1[0] && close1[1] < open1[1] && close1[2] < open1[2] )//三阴下降
	{
		price = SymbolInfoDouble(symbol, SYMBOL_ASK);
		// 计算开盘价与 price 的差值的绝对值
		double difference = MathAbs(openPrice - price);
		// 输出开盘价到日志
		Print("当前15分钟K线的开盘价是: ", openPrice);
		// 输出差值的绝对值到日志
		Print("开盘价与 ", price, " 的差值的绝对值是: ", difference);
		
		if( difference >= BIGDIFFER)
		{
			Print("超出了");
			return 3;
		}
		
		trade.Sell(lot, symbol, price);//做空
		TOP_price = price;
		MINI_price = price;
		order_flage = 0;
		Print("44444444空\r\n");
		return 2;
	}
	else if(close1[1] > open1[1] && close1[0] > open1[0] && (close1[0]-open1[0]) > DIFFER)//两阳上升
	{
		price = SymbolInfoDouble(symbol, SYMBOL_ASK);
		// 计算开盘价与 price 的差值的绝对值
		double difference = MathAbs(openPrice - price);
		// 输出开盘价到日志
		Print("当前15分钟K线的开盘价是: ", openPrice);
		// 输出差值的绝对值到日志
		Print("开盘价与 ", price, " 的差值的绝对值是: ", difference);
		
		if( difference >= BIGDIFFER)
		{
			Print("超出了");
			return 3;
		}
		
		trade.Buy(lot, symbol, price);//做多
		TOP_price = price;
		MINI_price = price;
		order_flage = 1;
		Print("aaaaaaaaa多\r\n");
		return 2;
	}
	else if(close1[1] < open1[1] && close1[0] < open1[0] && (open1[0]-close1[0]) > DIFFER)//两阴下降
	{
		price = SymbolInfoDouble(symbol, SYMBOL_ASK);
		// 计算开盘价与 price 的差值的绝对值
		double difference = MathAbs(openPrice - price);
		// 输出开盘价到日志
		Print("当前15分钟K线的开盘价是: ", openPrice);
		// 输出差值的绝对值到日志
		Print("开盘价与 ", price, " 的差值的绝对值是: ", difference);
		
		if( difference >= BIGDIFFER)
		{
			Print("超出了");
			return 3;
		}
		
		trade.Sell(lot, symbol, price);//做空
		TOP_price = price;
		MINI_price = price;
		order_flage = 0;
		Print("bbbbbbbbbbbbb空\r\n");
		return 2;
	}
	else if(close1[1] < open1[1] && close1[0] > open1[0] && close1[0] > open1[1] && (close1[0]-open1[0]) > DIFFER)//阳包阴
	{
		price = SymbolInfoDouble(symbol, SYMBOL_ASK);
		// 计算开盘价与 price 的差值的绝对值
		double difference = MathAbs(openPrice - price);
		// 输出开盘价到日志
		Print("当前15分钟K线的开盘价是: ", openPrice);
		// 输出差值的绝对值到日志
		Print("开盘价与 ", price, " 的差值的绝对值是: ", difference);
		
		if( difference >= BIGDIFFER)
		{
			Print("超出了");
			return 3;
		}
		
		trade.Buy(lot, symbol, price);//做多
		TOP_price = price;
		MINI_price = price;
		order_flage = 1;
		Print("ccccccccccc多\r\n");
		return 2;
	}
	else if(close1[1] > open1[1] && close1[0] < open1[0] && close1[0] < open1[1] && (open1[0]-close1[0]) > DIFFER)//阴包阳
	{
		price = SymbolInfoDouble(symbol, SYMBOL_ASK);
		// 计算开盘价与 price 的差值的绝对值
		double difference = MathAbs(openPrice - price);
		// 输出开盘价到日志
		Print("当前15分钟K线的开盘价是: ", openPrice);
		// 输出差值的绝对值到日志
		Print("开盘价与 ", price, " 的差值的绝对值是: ", difference);
		
		if( difference >= BIGDIFFER)
		{
			Print("超出了");
			return 3;
		}
		
		trade.Sell(lot, symbol, price);//做空
		TOP_price = price;
		MINI_price = price;
		order_flage = 0;
		Print("ddddddddddddd空\r\n");
		return 2;
	}
	else if(close1[0] > open1[0] && (close1[0]-open1[0]) > DIFFER)
	{
		price = SymbolInfoDouble(symbol, SYMBOL_ASK);
		// 计算开盘价与 price 的差值的绝对值
		double difference = MathAbs(openPrice - price);
		// 输出开盘价到日志
		Print("当前15分钟K线的开盘价是: ", openPrice);
		// 输出差值的绝对值到日志
		Print("开盘价与 ", price, " 的差值的绝对值是: ", difference);
		
		if( difference >= BIGDIFFER)
		{
			Print("超出了");
			return 3;
		}
		
		trade.Buy(lot, symbol, price);//做多
		TOP_price = price;
		MINI_price = price;
		order_flage = 1;
		Print("55555555多\r\n");
		return 2;
	}
	else if(close1[0] < open1[0] && (open1[0]-close1[0]) > DIFFER)
	{
		price = SymbolInfoDouble(symbol, SYMBOL_ASK);
		// 计算开盘价与 price 的差值的绝对值
		double difference = MathAbs(openPrice - price);
		// 输出开盘价到日志
		Print("当前15分钟K线的开盘价是: ", openPrice);
		// 输出差值的绝对值到日志
		Print("开盘价与 ", price, " 的差值的绝对值是: ", difference);
		
		if( difference >= BIGDIFFER)
		{
			Print("超出了");
			return 3;
		}
		
		trade.Sell(lot, symbol, price);//做空
		TOP_price = price;
		MINI_price = price;
		order_flage = 0;
		Print("66666666空\r\n");
		return 2;
	}//还需优化
	return 1;//返回本身
}

int CycleEnds(void)
{
    // 获取当前15分钟K线的开盘时间
    datetime currentKLineOpenTime = iTime(symbol, PERIOD, 0);

    // 获取最新的15分钟K线的开盘时间
    datetime newKLineOpenTime = iTime(symbol, PERIOD, 0);

	double price = SymbolInfoDouble(symbol, SYMBOL_ASK);
	//printf("price == %lf\r\n",price);

    // 如果最新的K线开盘时间和之前记录的不同，说明当前K线已经结束
    if (newKLineOpenTime != currentKLineOpenTime) 
	{
	Print("999999结束这根了\r\n");
        return 1;
    }

    // 短暂休眠，避免CPU占用过高
    //Sleep(100);
    
	return 3;
}
// 新增函数：在当前15分钟K线结束时返回1，并一直打印目前价格
int WaitFor15MinuteKLineEnd() {
    // 获取当前15分钟K线的开盘时间
    datetime currentKLineOpenTime = iTime(symbol, PERIOD, 0);

    while (true)
	{
        // 获取最新的15分钟K线的开盘时间
        datetime newKLineOpenTime = iTime(symbol, PERIOD, 0);

        // 获取当前市场价格
        double currentPrice = SymbolInfoDouble(symbol, SYMBOL_ASK);
        // 打印当前价格
        //Print("当前 " + symbol + " 的价格: " + DoubleToString(currentPrice));

        // 如果最新的K线开盘时间和之前记录的不同，说明当前K线已经结束
        if (newKLineOpenTime != currentKLineOpenTime) 
		{
            return 1;
        }

        // 短暂休眠，避免CPU占用过高
        Sleep(100);
    }
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
// OnTick 函数是专家顾问（EA）在每个新的报价到来时调用的函数
void OnTick()
{
	//while(1)
	{
		//短暂休眠，避免CPU占用过高
	    //Sleep(100);
		switch (next) 
		{
	        case 0:
		        next = OrderDetermination();//判断是否有单
		        break;
	        case 1:
	            next = PlaceOrder();//下单操作
	            break;
	        case 2:
	            next = PriceJudgment();//是否卖出
	            break;
	        case 3:
	            next = WaitFor15MinuteKLineEnd();//结束这一根
	            break;
	        case 4:
	            printf("星期四\n");
	            break;
	        case 5:
	            printf("星期五\n");
	            break;
	        case 6:
	            printf("星期六\n");
	            break;
	        case 7:
	            printf("星期日\n");
	            break;
	        default:
	            printf("输入的数字不在1 - 7范围内\n");
	    }
	}
}
